/* ex: set tabstop=4 shiftwidth=4 expandtab: */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "rtsp.h"
#include "rtsp_proto.h"
#include "lfxmsg.h"
#include "http.h"
#include "base64.h"
#include "nonce.h"

char netav_host[128] = {0};

static int parse_request(rtsp_channel_t *ch, char *buf, int len);
static int parse_post_request(rtsp_channel_t *ch, char *buf, int len);

static rtsp_session_t sess_conn;
static rtsp_session_t sess_lfxmsg;

static rtsp_channel_t channels[4]; /* up to 4 channels at one time */
static int chn_idx = 0;

static char sendbuf[8192];

static void
session_free(rtsp_session_t *sess)
{
	memset(sess, 0, sizeof(rtsp_session_t));
}

void
rtsp_init(void)
{
	session_free(&sess_conn);
	session_free(&sess_lfxmsg);
	sess_conn.type = RTSP_T_CONN;
	sess_lfxmsg.type = RTSP_T_LFXMSG;

	memset(channels, 0, sizeof(channels));
	chn_idx = 0;
}

int
open_channel(int fd)
{
	rtsp_channel_t *ch;

	if (chn_idx > 3)
		return -1;
	ch = &channels[chn_idx++];
	ch->fd = fd;

	return 0;
}


int
channel_read(int fd, void *unused)
{
	static char recvbuf[CHANNEL_BUF_SIZE];

	rtsp_channel_t *ch = &channels[0];
	int i, len;

	for (i = 0; i < 4; ch++, i++) {
		if (fd == ch->fd)
			goto found;
	}

	return -1; /* not found */

found:
	len = recv(fd, recvbuf, CHANNEL_BUF_SIZE, 0);
	if (len == -1) {
		/* teardown both sessions!
		 * so that other sockets will fail above and get closed
		 */
		rtsp_init();
		return -1;
	} else if (len == 0)
		return 0;

	recvbuf[len] = '\0';

	return parse_request(ch, recvbuf, len);
}

/*
 * There are 4 TCP connections during a session.
 *
 * 1. RTP downlink
 * C: GET /netav/message0 HTTP/1.1
 * S: HTTP/1.1 401 Unauthorized
 *    WWW-Authenticate: Digest realm="NetAV", nonce="8a594785107fe51532d9305380d3d040", algorithm=MD5, domain="/netav/", qop="auth",
 * C: GET /netav/message0 HTTP/1.1
 * S: HTTP/1.1 200 OK
 *    Authentication-Info: qop="auth", rspauth="none", cnonce="273366f37e34e0b8c07aa2cff639cf22", nc=00000001
 *    Server:  model=LF-V30US, modeltype=3, generation=3, support=1/2/3, scaling=1, codec=0/2/5, id=+xLIU9fbdHrA
 * C: GET /connect HTTP/1.1
 * S: HTTP/1.0 200 OK
 * S: RTSP/1.0 200 OK
 *
 * 2. uplink
 * C: GET /netav/message1 HTTP/1.1
 * S: HTTP/1.1 200 OK
 *    Authentication-Info: qop="auth", rspauth="NetAV", cnonce="273366f37e34e0b8c07aa2cff639cf22", nc=00000002
 * C: POST /connect HTTP/1.1
 * C: base64 encoded stuff
 *
 * 3. downlink
 * C: GET /netav/message2 HTTP/1.1
 * S: HTTP/1.1 200 OK
 *    Authentication-Info: qop="auth", rspauth="NetAV", cnonce="273366f37e34e0b8c07aa2cff639cf22", nc=00000003
 * C: GET /lfxmsg HTTP/1.1
 * S: HTTP/1.0 200 OK
 * S: LFX_MSG * RTSP/1.0
 *
 * 4. uplink
 * C: GET/netav/message3 HTTP/1.1
 * S: HTTP/1.1 200 OK
 *    Authentication-Info: qop="auth", rspauth="NetAV", cnonce="273366f37e34e0b8c07aa2cff639cf22", nc=00000004
 * C: POST /lfxmsg HTTP/1.1
 * C: base64 encoded stuff
 */


#define MSG0_UNAUTH_RESP \
	"HTTP/1.1 401 Unauthorized\r\n"\
	"WWW-Authenticate: Digest realm=\"NetAV\", nonce="\
	NONCE\
	", algorithm=MD5, domain=\"/netav/\", qop=\"auth\",\r\n"\
	"Pragma: no-cache\r\n"\
	"Cache-control: no-cache, no-store\r\n"\
	"\r\n"

#define MSG0_AUTH_RESP \
	"HTTP/1.1 200 OK\r\n"\
	"Authentication-Info: qop=\"auth\", rspauth=\"none\", cnonce=\"%s\", nc=00000001\r\n"\
	"Pragma: no-cache\r\n"\
	"Cache-control: no-cache, no-store\r\n"\
	"Server:  model=LF-V30US, modeltype=3, generation=3, support=1/2/3, scaling=1, codec=0/2, id=+xLIU9fbdHrA\r\n"\
	"\r\n"

#define MSGX_RESP \
	"HTTP/1.1 200 OK\r\n"\
	"Authentication-Info: qop=\"auth\", rspauth=\"NetAV\", cnonce=\"%s\", nc=\"%s\"\r\n"\
	"Pragma: no-cache\r\n"\
	"Cache-control: no-cache, no-store\r\n"\
	"\r\n"

#define TUNNEL_RESP \
	"HTTP/1.0 200 OK\r\n"\
	"Server: netAV\r\n"\
	"Connection: close\r\n"\
	"Date: Thu, 10 Jul 1980 10:30:00 GMT\r\n"\
	"Cache-Control: no-store\r\n"\
	"Pragma: no-cache\r\n"\
	"Content-Type: application/x-rtsp-tunnelled\r\n"\
	"\r\n"

static int
parse_request(rtsp_channel_t *ch, char *buf, int len)
{
	static char hdr[1024];

	int r, fd, offset;
	char *headers, cnonce[128], nc[32], *payload;
	http_path_t path;
	http_method_t method;

	fd = ch->fd;

	if ((r = http_parse_request(buf, len, &method, &path, &headers)) == -1) {
		return parse_post_request(ch, buf, len);
	}

	switch (path) {
	case HTTP_PATH_MESSAGE0:
		if (method != HTTP_METHOD_GET)
			return -1;
		if (ch->session)
			return -1;
		/* response 401 if header doesn't have cnonce, else reply 200 */
		if (http_get_header(headers, "Authorization", hdr) &&
				http_get_param(hdr, "cnonce", cnonce)) {
			r = sprintf(sendbuf, MSG0_AUTH_RESP, cnonce);
			send(fd, sendbuf, r, 0);
		} else {
			http_get_header(headers, "Host", netav_host);
			send(fd, MSG0_UNAUTH_RESP, strlen(MSG0_UNAUTH_RESP), 0);
		}
		break;

	case HTTP_PATH_MESSAGE1:
	case HTTP_PATH_MESSAGE2:
	case HTTP_PATH_MESSAGE3:
		if (method != HTTP_METHOD_GET)
			return -1;
		if (ch->session)
			return -1;
		if (!http_get_header(headers, "Authorization", hdr))
			return -1;
		if (!http_get_param(hdr, "cnonce", cnonce))
			return -1;
		if (!http_get_param(hdr, "nc", nc))
			return -1;
		r = sprintf(sendbuf, MSGX_RESP, cnonce, nc);
		send(fd, sendbuf, r, 0);

		/* if it's msg3, then we should start sending LFX_MSG req */
		if (path == HTTP_PATH_MESSAGE3)
			lfxmsg_start(&sess_lfxmsg);
		break;

	case HTTP_PATH_CONNECT:
		if (!ch->session)
			ch->session = &sess_conn;
		if (method == HTTP_METHOD_GET) {
			sess_conn.chn_get = ch;
			send(fd, TUNNEL_RESP, strlen(TUNNEL_RESP), 0);
			if (sess_conn.chn_post)
				sess_conn.state = RTSP_S_INIT;
		} else {
			sess_conn.chn_post = ch;
		}
		break;

	case HTTP_PATH_LFXMSG:
		if (!ch->session)
			ch->session = &sess_lfxmsg;
		if (method == HTTP_METHOD_GET) {
			sess_lfxmsg.chn_get = ch;
			send(fd, TUNNEL_RESP, strlen(TUNNEL_RESP), 0);
			if (sess_lfxmsg.chn_post)
				sess_lfxmsg.state = RTSP_S_INIT;
		} else {
			sess_lfxmsg.chn_post = ch;
		}
		break;

	default:
		return -1;
	}

	return 0;
}

static int
parse_post_request(rtsp_channel_t *ch, char *buf, int len)
{
	static char postbuf[CHANNEL_BUF_SIZE];

	rtsp_session_t *sess = ch->session;
	int cont_len;
	char *payload;

	if (!sess || sess->chn_post != ch)
		return -1;

	memset(postbuf, 0, sizeof(postbuf));

	if (base64_decode(postbuf, buf) == 0) {
		printf("BASE64 decode failure\n");
		return -1;
	}
	len = strlen(postbuf);
	if (ch->len + len + 1 > CHANNEL_BUF_SIZE)
		return -1;
	
	memcpy(&ch->buf[ch->len], postbuf, len + 1); /* last byte is 0 */
	len += ch->len;
	ch->len = len;

	if (len < 4)
		return 0;

	cont_len = http_get_content_length(ch->buf);
	if (cont_len > 0) {
		payload = http_get_payload(ch->buf);
		if (!payload || strlen(payload) < cont_len)
			return 0;
	} else {
		if (*(int *) (&ch->buf[len - 4]) != 0x0A0D0A0D)
			return 0;
	}

	printf("POST: %s\n", ch->buf);
	if (sess == &sess_conn)
		rtsp_parse_request(sess, ch, sendbuf);
	else
		lfxmsg_parse_request(sess, ch, sendbuf);

	return 0;
}
